#pragma once
#ifndef __LIT__SINGLETON__INCLUDED__HPP__
#define __LIT__SINGLETON__INCLUDED__HPP__

#include "LitPrerequisites.h"

namespace Lit
{
	template<typename T>
	class LIT_API_EXPORT_ONLY SingleThreaded {
	public : 
		typedef T VolatileType;

	protected : 
		inline explicit SingleThreaded() {}
		inline ~SingleThreaded() {}

	protected : 
		class LockThread {
		public : 
			inline explicit LockThread() {
				SingleThreaded::Lock();
			}
			inline ~LockThread() { 
				SingleThreaded::Unlock(); 
			}

		private : 
			inline explicit LockThread(LockThread const&) {}
			inline LockThread& operator=(LockThread const&) { return *this; }
		};    //    end of class LockThread

	private : 
		friend LockThread;

		inline static void Lock() {}
		inline static void Unlock() {}

	private : 
		inline explicit SingleThreaded(SingleThreaded const&) {}
		inline SingleThreaded& operator=(SingleThreaded const&) { return *this; }
	};    //    end of class SingleThreaded

	template<typename T>
	class LIT_API_EXPORT_ONLY DefaultLifetime {
	protected : 
		inline explicit DefaultLifetime() {}
		inline ~DefaultLifetime() {}

		inline static void OnDeadReference() { 
			throw std::logic_error("Dead Reference Detected"); 
		}
		inline static void ScheduleForDestruction(void (*pFun)()) {
			std::atexit(pFun); 
		}

	private : 
		inline explicit DefaultLifetime(DefaultLifetime const&) {}
		inline DefaultLifetime& operator=(DefaultLifetime const&) { return *this; }
	};    //    end of class DefaultLifetime

	template<typename T>
	class LIT_API_EXPORT_ONLY CreateUsingNew {
	protected : 
		inline explicit CreateUsingNew() {}
		inline ~CreateUsingNew() {}

		inline static T* CreateInstance() { return new T(); }
		inline static void DestroyInstance(T* t) { delete t; }

	private : 
		inline explicit CreateUsingNew(CreateUsingNew const&) {}
		inline CreateUsingNew& operator=(CreateUsingNew const&) { return *this; }
	};    //    end of class CreateUsingNew

	template<typename T, typename CreationPolicy = CreateUsingNew<T>, 
	template <typename> class LifetimePolicy = DefaultLifetime, 
	template <typename> class ThreadingModel = SingleThreaded>
	class LIT_API_EXPORT_ONLY Singleton : public CreationPolicy, public LifetimePolicy<T>, public ThreadingModel<T> {
	public : 
		static T& Instance();
		static void Destroy();

	protected : 
		inline explicit Singleton() { 
			assert(Singleton::instance_ == 0); 
			Singleton::instance_ = static_cast<T*>(this); 
			Singleton::destroyed_ = false; 
		}
		inline ~Singleton() { 
			Singleton::instance_ = 0; 
			Singleton::destroyed_ = true; 
		}

	private : 
		typename static T* instance_;
		static bool destroyed_;

	private : 
		inline explicit Singleton(Singleton const&) {}
		inline Singleton& operator=(Singleton const&) { return *this; }
	};    //    end of class Singleton

	template<typename T, typename C, template <typename> class L, template <typename> class TM>
	typename T&  Singleton<T, C, L, TM>::Instance() {
		if ( Singleton::instance_ == 0 ) {
			LockThread lock;
			if ( Singleton::instance_ == 0 ) {
				if ( Singleton::destroyed_ ) {
					OnDeadReference();
					Singleton::destroyed_ = false;
				}
				Singleton::instance_ = CreateInstance();
				try {
					ScheduleForDestruction(Singleton::Destroy);
				} catch(...) {
					DestroyInstance(Singleton::instance_);
				}        
			}
		}
		return *(Singleton::instance_);
	}

	template<typename T, typename C, template <typename> class L, template <typename> class TM>
	void Singleton<T, C, L, TM>::Destroy() {
		if ( Singleton::instance_ != 0 ) {
			LockThread lock;
			if ( Singleton::instance_ != 0 ) {
				DestroyInstance(Singleton::instance_);
				Singleton::instance_ = 0;
				Singleton::destroyed_ = true;
			}
		}
	}

	template<typename T, typename C, template <typename> class L, template <typename> class TM>
	typename Singleton<T, C, L, TM>:: VolatileType* Singleton<T, C, L, TM>::instance_ = 0;
	template<typename T, typename C, template <typename> class L, template <typename> class TM>
	bool Singleton<T, C, L, TM>::destroyed_ = false;

};
#endif